package com.manner.groupon.service.store.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import cn.hutool.jwt.JWTUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.manner.groupon.common.BaseResponse;
import com.manner.groupon.common.PageResult;
import com.manner.groupon.common.ResultUtils;
import com.manner.groupon.exception.CustomException;
import com.manner.groupon.mapper.*;
import com.manner.groupon.model.dto.linkman.StaffAddLinkmanRequest;
import com.manner.groupon.model.dto.search.AdminSearch;
import com.manner.groupon.model.entity.*;
import com.manner.groupon.model.enums.OrderStatus;
import com.manner.groupon.model.enums.RoleEnums;
import com.manner.groupon.model.vo.AdminAssayVO;
import com.manner.groupon.model.vo.AdminLinkManVO;
import com.manner.groupon.model.vo.LoginResultVo;
import com.manner.groupon.model.vo.StaffHomeData;
import com.manner.groupon.service.store.StoreLinkManService;
import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import static com.manner.groupon.constant.CommonConstant.LOGIN_SIGN;
import static com.manner.groupon.constant.CommonConstant.USER_LOGIN_PREV;
import static com.manner.groupon.constant.RedisCacheKeyPrefix.STAFF_INVITATION_ALL_LINKMAN;
import static com.manner.groupon.constant.SystemDictConstant.FIRST_ORDER_SCALE;

@Service
@RequiredArgsConstructor
public class StoreLinkManServiceImpl extends ServiceImpl<StoreLinkManMapper, StoreLinkMan> implements StoreLinkManService {

    @Resource
    private RedisTemplate<String, StoreLinkMan> redisTemplate;

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    private final StoreLinkManMapper storeLinkManMapper;

    private final StoreMapper storeMapper;

    private final UserMapper userMapper;

    private final GrouponCouponsMapper couponsMapper;

    private final GrouponOrderMapper grouponOrderMapper;

    private final WithDrawRecordMapper withDrawRecordMapper;

    private final SysDictMapper sysDictMapper;

    private final StoreAccountFlowMapper storeAccountFlowMapper;

    /**
     * @param request
     * @description: 平台内部员工增加商户门店联系人信息
     * @author Manner
     * @date 2024/11/22 16:14
     */
    @Transactional(rollbackFor = Throwable.class)
    @Override
    public void addStoreLinkMan(StaffAddLinkmanRequest request) {
        StoreLinkMan dataBaseIsExist = storeLinkManMapper.selectOne(new LambdaQueryWrapper<StoreLinkMan>()
                .eq(StoreLinkMan::getPhone, request.getPhone()));
        //判断新加的联系人手机号是否已经存在于数据库中了
        if (ObjectUtil.isNotEmpty(dataBaseIsExist)) {
            throw new CustomException("联系人手机号已存在!");
        }
        //根据分享人的内部员工手机号查询姓名
        String storeStaffName = storeLinkManMapper.selectOne(new LambdaQueryWrapper<StoreLinkMan>()
                .eq(StoreLinkMan::getPhone, request.getInvitePeoplePhone())).getLinkmanName();
        //记录中介推荐人的姓名
        StoreLinkMan storeLinkMan = BeanUtil.copyProperties(request, StoreLinkMan.class);
        storeLinkMan.setIntroducerName(storeStaffName);
        storeLinkMan.setRole(RoleEnums.STORE_ROLE.getId());
        storeLinkMan.setPassword(request.getPhone().substring(5, 11));
        //再将新建联系人的信息记录插入到数据库
        storeLinkManMapper.insert(storeLinkMan);
    }

    @Override
    public BaseResponse<LoginResultVo<StoreLinkMan>> login(String phone, String pwd) {
        //查询数据
        StoreLinkMan linkMan = query().eq("phone", phone).eq("password", pwd).one();
        //判断
        if (ObjUtil.isNull(linkMan)) {
            return ResultUtils.fail("用户名或密码错误");
        }
        //准备令牌参数
        Map<String, Object> map = new HashMap<>();
        map.put("id", linkMan.getId());
        map.put("phone", linkMan.getPhone());
        //登录成功,生成令牌
        String token = JWTUtil.createToken(map, LOGIN_SIGN.getBytes(StandardCharsets.UTF_8));
        //存入Redis
        redisTemplate.opsForValue().set(USER_LOGIN_PREV + token, linkMan);

        //根据登录手机账号查询出平台内部员工的姓名
        String staffName = storeLinkManMapper.selectOne(new LambdaQueryWrapper<StoreLinkMan>()
                .eq(StoreLinkMan::getPhone, phone)).getLinkmanName();

        //再根据姓名查询有多少联系人被邀请入驻
        List<StoreLinkMan> storeLinkManList = storeLinkManMapper.selectList(new LambdaQueryWrapper<StoreLinkMan>()
                .eq(StoreLinkMan::getIntroducerName, staffName)
        );
        //将联系人信息存入Redis,方便后续业务使用
        String cacheKey = STAFF_INVITATION_ALL_LINKMAN + phone;
        storeLinkManList.forEach(storeLinkMan -> {
            stringRedisTemplate.opsForList().rightPushAll(cacheKey, JSONUtil.toJsonStr(storeLinkMan));
        });
        stringRedisTemplate.expire(cacheKey, 30, TimeUnit.MINUTES);

        //返回令牌
        return ResultUtils.success(new LoginResultVo<>(linkMan, token));
    }

    @Override
    public BaseResponse<LoginResultVo<StoreLinkMan>> rootLogin(String phone, String pwd) {
        //查询数据
        StoreLinkMan linkMan = query().eq("phone", phone).one();
        //判断
        if (ObjUtil.isNull(linkMan)) {
            return ResultUtils.fail("用户名或密码错误");
        }
        //准备令牌参数
        Map<String, Object> map = new HashMap<>();
        map.put("id", linkMan.getId());
        map.put("phone", linkMan.getPhone());
        //登录成功,生成令牌
        String token = JWTUtil.createToken(map, LOGIN_SIGN.getBytes(StandardCharsets.UTF_8));
        //存入Redis
        redisTemplate.opsForValue().set(USER_LOGIN_PREV + token, linkMan);
        //返回令牌
        return ResultUtils.success(new LoginResultVo<>(linkMan, token));
    }

    @Override
    public BaseResponse<AdminAssayVO> query_assay() {
        //获取用户数量
        Long user = userMapper.selectCount(null);
        //获取商户数量
        Long store = storeMapper.selectCount(null);
        //获取团购券数量
        List<GrouponCoupons> list = couponsMapper.selectList(null);
        //定义数量
        long coupon = 0;
        //遍历
        for (GrouponCoupons grouponCoupons : list) {
            coupon = coupon + grouponCoupons.getStock();
        }
        //获取账面余额
        BigDecimal bookBalance = query().eq("role", RoleEnums.ADMIN_ROLE.getId()).one().getBookBalance();
        //获取总营业额
        List<GrouponOrder> orders = grouponOrderMapper.selectList(new QueryWrapper<GrouponOrder>().in("status", OrderStatus.HAVE_PAID.getCode(), OrderStatus.HAVE_BEEN_USED.getCode(),
                OrderStatus.HAVE_EXPIRED.getCode()));
        //定义金额
        BigDecimal price = new BigDecimal(0);
        //遍历
        for (GrouponOrder order : orders) {
            price = price.add(order.getTotalAmount());
        }
        //获取今天日期
        String format = DateTimeFormatter.ofPattern("yyyy-MM-dd").format(LocalDate.now());
        //查询今日订单
        List<GrouponOrder> orderList = grouponOrderMapper.selectList(new QueryWrapper<GrouponOrder>().gt("create_time", format));
        //定义数量
        BigDecimal dayOrder = new BigDecimal(orderList.size());
        BigDecimal dayCoupon = new BigDecimal(0);
        //判断是否为空
        for (GrouponOrder order : orderList) {
            BigDecimal bigDecimal = new BigDecimal(order.getTicketNumber());
            //获取
            dayCoupon = dayCoupon.add(bigDecimal);
        }
        //获取总提现金额
        List<WithDrawRecord> status = withDrawRecordMapper.selectList(new QueryWrapper<WithDrawRecord>().eq("status", 1));
        //定义金额
        BigDecimal draw = new BigDecimal(0);
        //遍历
        for (WithDrawRecord withDrawRecord : status) {
            draw = draw.add(withDrawRecord.getPrice());
        }
        //返回数据
        return ResultUtils.success(new AdminAssayVO(bookBalance, price, draw, dayOrder, dayCoupon, new BigDecimal(store), new BigDecimal(coupon), new BigDecimal(user)));
    }

    @Override
    public BaseResponse<PageResult<AdminLinkManVO>> query(AdminSearch adminSearch) {
        //构建分页
        Page<StoreLinkMan> page = new Page<>(adminSearch.getCurrent(), adminSearch.getPageSize());
        //构建查询条件
        QueryWrapper<StoreLinkMan> wrapper = new QueryWrapper<>();
        //判断
        if (!StrUtil.isNullOrUndefined(adminSearch.getName())) {
            //查询
            wrapper.like("linkman_name", '%' + adminSearch.getName() + '%');
        }
        wrapper.in("role", RoleEnums.STORE_ROLE.getId(), RoleEnums.STAFF_ROLE.getId(), RoleEnums.LIVE_ROLE.getId());
        //查询数据
        Page<StoreLinkMan> result = storeLinkManMapper.selectPage(page, wrapper);
        //准备容器
        List<AdminLinkManVO> container = new ArrayList<>();
        //遍历获取角色信息
        for (StoreLinkMan record : result.getRecords()) {
            container.add(new AdminLinkManVO(record.getId(), record.getLinkmanName(), record.getPhone(), record.getLinkmanName(), record.getRemitAccount(), record.getBankName(),
                    record.getPayeeName(), record.getBookBalance(), record.getPosition(), record.getPayeeName(), record.getScale(), record.getIntroducerName(), record.getRole(), record.getIsDelete(), record.getCreateTime(), record.getUpdateTime(),
                    RoleEnums.getInstance(record.getRole()).getName()));
        }
        //返回数据
        return ResultUtils.success(new PageResult<>(container, result.getTotal()));
    }

    /**
     * @param staffPhone 员工手机账号
     * @return StaffHomeData
     * @description: 加载后台员工的主页数据
     * @author Manner
     * @date 2024/12/11 18:47
     */
    @Override
    public StaffHomeData loadStaffHomeData(String staffPhone) {
        //创建返回数据的容器对象
        StaffHomeData staffHomeData = new StaffHomeData();
        SysDict sysDict = sysDictMapper.selectOne(new LambdaQueryWrapper<SysDict>().eq(SysDict::getName, FIRST_ORDER_SCALE));
        staffHomeData.setProfit(Integer.valueOf(sysDict.getValue()));

        StoreLinkMan storeLinkMan = storeLinkManMapper.selectOne(new LambdaQueryWrapper<StoreLinkMan>()
                .eq(StoreLinkMan::getPhone, staffPhone));

        LocalDate currentDate = LocalDate.now();
        BigDecimal toDayRevenue = storeAccountFlowMapper.selectList(new LambdaQueryWrapper<StoreAccountFlow>()
                        .eq(StoreAccountFlow::getUserId, storeLinkMan.getId())
                        .between(StoreAccountFlow::getCreateTime, currentDate, currentDate)
                ).stream()
                .map(storeAccountFlow -> storeAccountFlow.getAmount())
                .reduce(BigDecimal.ZERO, BigDecimal::add);
        staffHomeData.setToDayDivideInto(toDayRevenue);

        Integer invitePeopleNumber = storeLinkManMapper.selectList(new LambdaQueryWrapper<StoreLinkMan>()
                .eq(StoreLinkMan::getIntroducerName, storeLinkMan.getLinkmanName())).size();
        staffHomeData.setInvitePeopleNumber(invitePeopleNumber);
        return staffHomeData;
    }
}
